package foodmaterial.service.impl;

import foodmaterial.dao.DocumentTrackDao;
import foodmaterial.dao.FoodMaterialDao;
import foodmaterial.dao.StockDao;
import foodmaterial.dto.ProcessBillDto;
import foodmaterial.dto.ProductionLineDto;
import foodmaterial.dto.PurchasePlanDto;
import foodmaterial.dto.StockBillDto;
import foodmaterial.entity.*;
import foodmaterial.service.StockService;
import foodmaterial.utils.Arithmetic;
import foodmaterial.utils.Constants;
import foodmaterial.utils.SnowflakeWorker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

@Service
public class StockServiceImpl extends BaseService implements StockService {

    @Autowired
    private StockDao dao ;

    @Autowired
    private FoodMaterialDao foodMaterialDao ;

    @Autowired
    private DocumentTrackDao documentTrackDao ;

    private List<FoodMaterial> getFoodMaterial(List<StockBillDetail> details,int dir) {
        List<FoodMaterial> list = new ArrayList<>() ;
        for (StockBillDetail detail : details) {
            FoodMaterial foodMaterial = new FoodMaterial() ;
            foodMaterial.setId(detail.getFoodMaterialId());
            foodMaterial.setAmount(new BigDecimal(detail.getAmount() * dir));
            list.add(foodMaterial) ;
        }
        Collections.sort(list);
        return list ;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addStockBill(StockBill stockBill) {
        stockBill.setId(nextId());
        stockBill.setOperationPerson(this.getSysUser().getId());
        stockBill.setOperationDate(new Date());
        List<StockBillDetail> details = stockBill.getDetail() ;
        List<FoodMaterial> list = getFoodMaterial(details,1) ;
        for (StockBillDetail detail : details) {
            detail.setId(nextId());
            detail.setStockBillId(stockBill.getId());
        }
        dao.insertStockBill(stockBill) ;
        dao.insertStockBillDetail(details) ;
        return foodMaterialDao.updateFoodMaterialStock(list) ;
    }

    private StockBillDetail getStockBillDetail(List<StockBillDetail> details,Long id) {
        for (StockBillDetail detail : details) {
            if (detail.getFoodMaterialId().longValue() == id.longValue()) return detail ;
        }
        return null ;
    }

    private void compareDetail(StockBillDetail newDetail,StockBillDetail oldDetail,StringBuilder sb) {
        if (newDetail.getAmount().intValue() != oldDetail.getAmount().intValue()) {
            sb.append("修改了食材[").append(oldDetail.getFoodMaterialName())
                    .append("]的数量，改前：")
                    .append(oldDetail.getAmount())
                    .append("，改后：").append(newDetail.getAmount()).append("；");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int modifyStockBill(StockBill stockBill) {
        SysUser userDetails = this.getSysUser();
        if (stockBill.getOperationPerson().longValue() != userDetails.getId().longValue()) {
            stockBill.setLastOperationPerson(userDetails.getId());
            stockBill.setLastOperationDate(new Date());
        } else {
            stockBill.setOperationDate(new Date());
        }
        List<StockBillDetail> details = dao.selectStockBillDetail(stockBill.getId()) ;

        StringBuilder sb = new StringBuilder() ;
        for (StockBillDetail oldDetail : details) {
            StockBillDetail findDetail = this.getStockBillDetail(stockBill.getDetail(),
                    oldDetail.getFoodMaterialId()) ;
            if (findDetail != null) compareDetail(findDetail,oldDetail,sb) ;
            else { //老明细中存在的食材在新明细中不存在
                sb.append("删除了食材[").append(oldDetail.getFoodMaterialName()).append("]，数量是：");
                sb.append(oldDetail.getAmount()).append("；");
            }
        }

        for (StockBillDetail newDetail : stockBill.getDetail()) {
            StockBillDetail findDetail = this.getStockBillDetail(details,newDetail.getFoodMaterialId()) ;
            if (findDetail == null) { //老明细中不存在的食材在新明细中存在
                sb.append("添加了食材[").append(newDetail.getFoodMaterialName())
                        .append("]，数量是：").append(newDetail.getAmount())
                        .append("；");
            }
        }

        if (sb.length() > 0) {
            DocumentTrack track = this.getDocumentTrack(stockBill.getId(),4,sb) ;
            documentTrackDao.insertDocumentTrack(track) ;
        }

        List<FoodMaterial> list = getFoodMaterial(details,-1) ;
        foodMaterialDao.updateFoodMaterialStock(list) ;//根据老数据恢复之前的库存
        details = stockBill.getDetail() ;
        for (StockBillDetail detail : details) {
            detail.setId(nextId());
            detail.setStockBillId(stockBill.getId());
        }
        list = getFoodMaterial(details,1) ;
        dao.deleteStockBillDetail(stockBill.getId()) ;
        dao.updateStockBill(stockBill) ;
        dao.insertStockBillDetail(details) ;
        return foodMaterialDao.updateFoodMaterialStock(list) ;//根据新数据调整库存
    }

    @Override
    public List<StockBill> queryStockBill(StockBillDto dto) {
        if (dto.getUpdateSelf() != null && dto.getUpdateSelf()) {
            dto.setOperationPerson(this.getSysUser().getId()) ;
        }
        return dao.selectStockBill(dto);
    }

    @Override
    public StockBill queryModifyBill(Long billId) {
        return dao.selectModifyBill(billId);
    }

    @Override
    public List<StockBillDetail> queryStockBillDetail(Long billId) {
        return dao.selectStockBillDetail(billId);
    }

    @Override
    public List<StockBill> queryPrintStockBill(List<Long> billIds) {
        return dao.selectPrintStockBill(billIds);
    }

    @Override
    public String addProductionLine(ProductionLine productionLine) {
        productionLine.setId(nextId());
        int rows = dao.insertProductionLine(productionLine);
        if (rows <= 0) return "新增生产线失败" ;
        else return null ;
    }

    @Override
    public String modifyProductionLine(ProductionLine productionLine) {
        int rows = dao.updateProductionLine(productionLine);
        if (rows <= 0) return "修改生产线失败" ;
        else return null ;
    }

    @Override
    public List<ProductionLine> queryProductionLine(ProductionLineDto dto) {
        return dao.selectProductionLine(dto);
    }

    private List<FoodMaterial> getFoodMaterial(List<ProcessBillDetail> details,
             List<FoodMaterial> process,int originalDir,int processDir) {
        Map<Long,FoodMaterial> map = new HashMap<>() ;
        List<FoodMaterial> original = new ArrayList<>() ;
        for (ProcessBillDetail detail : details) {
            if (map.containsKey(detail.getOriginalId())) { //净菜对应的毛菜
                FoodMaterial fm = map.get(detail.getOriginalId()) ;
                fm.setAmount(Arithmetic.add(fm.getAmount(),Arithmetic.mul(detail.getOriginalAmount() , originalDir ,3),3));
            } else {
                FoodMaterial fm = new FoodMaterial() ;
                fm.setId(detail.getOriginalId());
                fm.setAmount(Arithmetic.mul(detail.getOriginalAmount() , originalDir ,3));
                map.put(detail.getOriginalId(),fm) ;
            }
            FoodMaterial fm = new FoodMaterial() ;
            fm.setId(detail.getProcessId());
            fm.setAmount(Arithmetic.mul(detail.getProcessAmount() , processDir , 3));
            process.add(fm) ;
        }
        original.addAll(map.values()) ;
        Collections.sort(original);
        Collections.sort(process);
        return original ;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addProcessBill(ProcessBill processBill) {
        processBill.setId(nextId());
        processBill.setOperationPerson(this.getSysUser().getId());
        processBill.setOperationDate(new Date());
        List<ProcessBillDetail> details = processBill.getDetail() ;
        List<FoodMaterial> process = new ArrayList<>() ;
        List<FoodMaterial> original = getFoodMaterial(details,process,-1,1) ;
        this.foodMaterialDao.updateFoodMaterialStock(original) ;//减毛菜库存
        this.foodMaterialDao.updateFoodMaterialStock(process) ;//加净菜库存
        for (ProcessBillDetail detail : details) {
            detail.setId(nextId());
            detail.setProcessBillId(processBill.getId());
            detail.setStockDate(processBill.getStockDate());
            detail.setProductionLineId(processBill.getProductionLineId());
        }
        dao.insertProcessBill(processBill) ;
        return dao.insertProcessBillDetail(details);
    }

    private ProcessBillDetail getProcessBillDetail(List<ProcessBillDetail> details,Long id) {
        for (ProcessBillDetail detail : details) {
            if (detail.getProcessId().longValue() == id.longValue()) return detail ;
        }
        return null ;
    }

    private void compareDetail(ProcessBillDetail newDetail,ProcessBillDetail oldDetail,StringBuilder sb) {
        if (newDetail.getProcessAmount().intValue() != oldDetail.getProcessAmount().intValue() ||
            newDetail.getOriginalAmount().intValue() != oldDetail.getOriginalAmount().intValue()) {
            sb.append("修改了净菜[").append(oldDetail.getProcessName())
                    .append("]的数量，改前：")
                    .append(oldDetail.getProcessAmount())
                    .append("，改后：").append(newDetail.getProcessAmount());
            sb.append("，修改了毛菜[").append(oldDetail.getOriginalName())
                    .append("]的数量，改前：")
                    .append(oldDetail.getOriginalAmount())
                    .append("，改后：").append(newDetail.getOriginalAmount()).append("；");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int modifyProcessBill(ProcessBill processBill) {
        SysUser userDetails = this.getSysUser();
        if (processBill.getOperationPerson().longValue() != userDetails.getId().longValue()) {
            processBill.setLastOperationPerson(userDetails.getId());
            processBill.setLastOperationDate(new Date());
        } else {
            processBill.setOperationDate(new Date());
        }
        List<ProcessBillDetail> details = dao.selectProcessBillDetail(processBill.getId()) ;

        StringBuilder sb = new StringBuilder() ;
        for (ProcessBillDetail oldDetail : details) {
            ProcessBillDetail findDetail = this.getProcessBillDetail(processBill.getDetail(),
                    oldDetail.getProcessId()) ;
            if (findDetail != null) compareDetail(findDetail,oldDetail,sb) ;
            else { //老明细中存在的食材在新明细中不存在
                sb.append("删除了净菜[").append(oldDetail.getProcessName()).append("]，数量是：");
                sb.append(oldDetail.getProcessAmount())
                        .append("，毛菜[").append(oldDetail.getOriginalName())
                        .append("]，数量是：");
                sb.append(oldDetail.getOriginalAmount()).append("；");
            }
        }

        for (ProcessBillDetail newDetail : processBill.getDetail()) {
            ProcessBillDetail findDetail = this.getProcessBillDetail(details,newDetail.getProcessId()) ;
            if (findDetail == null) { //老明细中不存在的食材在新明细中存在
                sb.append("添加了净菜[").append(newDetail.getProcessName())
                        .append("]，数量是：").append(newDetail.getProcessAmount())
                        .append("，毛菜[").append(newDetail.getOriginalName())
                        .append("]，数量是：").append(newDetail.getOriginalAmount())
                        .append("；");
            }
        }

        if (sb.length() > 0) {
            DocumentTrack track = this.getDocumentTrack(processBill.getId(),3,sb) ;
            documentTrackDao.insertDocumentTrack(track) ;
        }

        List<FoodMaterial> process = new ArrayList<>() ;
        List<FoodMaterial> original = getFoodMaterial(details,process,1,-1) ;
        this.foodMaterialDao.updateFoodMaterialStock(original) ;//加毛菜库存
        this.foodMaterialDao.updateFoodMaterialStock(process) ;//减净菜库存
        process.clear();
        details = processBill.getDetail() ;
        original = getFoodMaterial(details,process,-1,1) ;
        this.foodMaterialDao.updateFoodMaterialStock(original) ;//减毛菜库存
        this.foodMaterialDao.updateFoodMaterialStock(process) ;//加净菜库存
        dao.deleteProcessBillDetail(processBill.getId()) ;
        dao.updateProcessBill(processBill) ;
        for (ProcessBillDetail detail : details) {
            detail.setId(nextId());
            detail.setProcessBillId(processBill.getId());
            detail.setStockDate(processBill.getStockDate());
            detail.setProductionLineId(processBill.getProductionLineId());
        }
        return dao.insertProcessBillDetail(details);
    }

    @Override
    public List<ProcessBill> queryProcessBill(ProcessBillDto dto) {
        if (dto.getUpdateSelf() != null && dto.getUpdateSelf()) {
            dto.setOperationPerson(this.getSysUser().getId()) ;
        }
        return dao.selectProcessBill(dto);
    }

    @Override
    public ProcessBill queryModifyProcessBill(Long processBillId) {
        return dao.selectModifyProcessBill(processBillId);
    }

    @Override
    public List<ProcessBillDetail> queryProcessBillDetail(Long processBillId) {
        return dao.selectProcessBillDetail(processBillId);
    }

    @Override
    public List<ProcessBillDetail> queryTotalProcess(ProcessBillDto dto) {
        return dao.selectTotalProcess(dto);
    }

    @Override
    public List<ProcessBill> queryPrintProcessBill(List<Long> billIds) {
        return dao.selectPrintProcessBill(billIds);
    }

    @Override
    public List<ProcessBillDetail> queryProcessPlan(PurchasePlanDto dto) {
        dto.setKitchen(this.getSysUser().getFoodSupplyType().equals(Constants.KITCHEN_FOOD));
        return dao.selectProcessPlan(dto);
    }
}
